﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using VideoReflector.Models;
using VideoReflector.Views;

namespace VideoReflector.Controllers
{
    /// <summary>
    /// Controller tasked with displaying clip tags on time-line, review and editing of individual clip details
    /// and playing clips via ClipPlayer
    /// </summary>
    public class ReviewSessionController : NotifyingObject
    {
        #region Private fields

        private Session _session;
        private ApplicationSettings _settings;
        private ClipPlayerController _playerController;
        private Panel _videoDisplayContainer;
        private ClipTag _currentClipTag;

        #endregion // Private fields

        #region Constructor

        /// <summary>
        /// Constructor for ReviewSessionController
        /// </summary>
        /// <param name="session"> Data associated with session being reviewed </param>
        /// <param name="settings"> Current application settings </param>
        /// <param name="videoDisplayContainer"> WPF Panel object which video output should be displayed in </param>
        public ReviewSessionController(Session session, ApplicationSettings settings, Panel videoDisplayContainer)
        {
            _session = session;
            _settings = settings;
            _videoDisplayContainer = videoDisplayContainer;
            // set up clip player to play all clips by default
            PlayClips(Session.ClipTags, false);
            // add collection views of tags associated with each group to ClipTagGroup objects
            PopulateTagGroupViews();
        }

        #endregion // Constructor

        #region Helpers

        /// <summary>
        /// Add ICollectionView of Session clip tags filtered by group ID to each tag group (defined in SchemaExtensions)
        /// </summary>
        private void PopulateTagGroupViews()
        {
            foreach (var tagGroup in Session.ClipTagGroups)
            {
                // ICollectionView.CurrentItem does not appear to be set when Filter is applied, hence filter
                // clip tags prior to creating CollectionView rather than afterwards
                var filteredTags = Session.ClipTags.Where(tag => tag.GroupID == tagGroup.ID).ToList();
                var view = new ListCollectionView(filteredTags);
                // synchronise CurrentClipTag on change of view current item
                view.CurrentChanged += (s, e) => CurrentClipTag = (ClipTag)((ICollectionView)s).CurrentItem;
                tagGroup.ClipTagsView = view;
            }
        }

        /// <summary>
        /// Create a new window with a ToolWindow window style to host a subsidiary view and controller to
        /// the current top level task controller
        /// </summary>
        /// <param name="controller"> Controller for task being started </param>
        /// <param name="view"> View for task being started </param>
        /// <param name="title"> Title to give window </param>
        /// <returns> Window with content set to supplied view and data context set to supplied controller </returns>
        private Window CreateToolWindow(TaskController controller, UserControl view, string title)
        {
            var window = new Window();
            window.WindowStyle = WindowStyle.ToolWindow;
            window.SizeToContent = SizeToContent.WidthAndHeight;
            window.Content = view;
            controller.Finished += (s, e) =>
            {
                window.Dispatcher.BeginInvoke(new Action(() =>
                    {
                       if(window.DialogResult == null)
                           window.DialogResult = !e.wasCancelled;
                        window.Close();
                    }));
            };
            window.DataContext = controller;
            window.Title = title;
            return window;
        }

        #endregion // Helpers

        #region Public properties

        /// <summary>
        /// Public accessor for current session data
        /// </summary>
        public Session Session
        {
            get { return _session; }
        }

        /// <summary>
        /// Public accessor for clip player controller which exposes commands for control of clip playback
        /// </summary>
        public ClipPlayerController PlayerController
        {
            get         
            { 
                return _playerController;  
            }
            private set 
            {
                if (_playerController != value)
                {
                    if (_playerController != null)
                        _playerController.ClipPlayer.Dispose();
                    _playerController = value;
                    NotifyPropertyChanged("PlayerController");
                }
            }
        }

        /// <summary>
        /// Public accessor for current (i.e. selected and/or playing) ClipTag object
        /// </summary>
        public ClipTag CurrentClipTag
        {
            get 
            { 
                return _currentClipTag; 
            }
            set
            {
                _currentClipTag = value;
                NotifyPropertyChanged("CurrentClipTag");
            }
        }

        #endregion // Public properties

        #region Commands

        #region PlayClips

        private RelayCommand _playClipsCommand;

        /// <summary>
        /// Play a selection of video clips on screen corresponding to the ClipTag collection 
        /// passed as a command parameter
        /// </summary>
        public ICommand PlayClipsCommand
        {
            get
            {
                if (_playClipsCommand == null)
                {
                    _playClipsCommand = new RelayCommand
                    (
                        param => this.PlayClips(param),
                        param => this.CanPlayClips(param)
                    );
                }
                return _playClipsCommand;
            }
        }

        private void PlayClips(object param, bool startPlaying = true)
        {
            var tags = (IList<ClipTag>)param;
            PlayerController = new ClipPlayerController(tags, _session.VideoFile, _session.SaveDirectory, _videoDisplayContainer);
            PlayerController.ClipPlayer.ClipChanged += (s, e) => CurrentClipTag = e.NewClipTag;
            if(startPlaying)
                PlayerController.ClipPlayer.Play();
        }

        private bool CanPlayClips(object param)
        {
            return param != null && param is IList<ClipTag> && ((IList<ClipTag>)param).Count > 0;
        }

        #endregion // PlayClips

        #region ExportClips

        private RelayCommand _exportClipsCommand;

        /// <summary>
        /// Export a selection of clips to file corresponding to the ClipTag collection 
        /// passed as a command parameter
        /// </summary>
        public ICommand ExportClipsCommand
        {
            get
            {
                if (_exportClipsCommand == null)
                {
                    _exportClipsCommand = new RelayCommand
                    (
                        param => this.ExportClips(param),
                        param => this.CanExportClips(param)
                    );
                }
                return _exportClipsCommand;
            }
        }

        private void ExportClips(object param)
        {
            var tags = (IList<ClipTag>)param;
            var controller = new ClipExporterController(tags, _session.VideoFile, _session.SaveDirectory);
            var view = new ClipExporterView();
            var window = CreateToolWindow(controller, view, "Export session video clips");
            if (window.ShowDialog() == true)
            {
                MessageBox.Show("Successfully finished exporting clips", "Finished Exporting",
                                MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private bool CanExportClips(object param)
        {
            return CanPlayClips(param);
        }

        #endregion // ExportClips  

        #region EditSessionDetails

        private RelayCommand _editSessionDetailsCommand;

        /// <summary>
        /// Edit the details such as therapist and group name of the current session
        /// </summary>
        public ICommand EditSessionDetailsCommand
        {
            get
            {
                if (_editSessionDetailsCommand == null)
                {
                    _editSessionDetailsCommand = new RelayCommand
                    (
                        param => this.EditSessionDetails(),
                        param => this.CanEditSessionDetails()
                    );
                }
                return _editSessionDetailsCommand;
            }
        }

        private void EditSessionDetails()
        {
            var controller = new EditSessionDetailsController(_session);
            var view = new EditSessionDetailsView();
            var window = CreateToolWindow(controller, view, "Edit session details");
            if (window.ShowDialog() == true)
                _session.Save();
        }

        private bool CanEditSessionDetails()
        {
            return true;
        }

        #endregion // EditSessionDetails  
                
        #region AddTag

        private RelayCommand _addTagCommand;

        /// <summary>
        /// Add a new clip tag to the ClipTagGroup with the ID passed as a Command Parameter
        /// </summary>
        public ICommand AddTagCommand
        {
            get
            {
                if (_addTagCommand == null)
                {
                    _addTagCommand = new RelayCommand
                    (
                        param => this.AddTag(param),
                        param => this.CanAddTag(param)
                    );
                }
                return _addTagCommand;
            }
        }

        private void AddTag(object param)
        {
            var tagGroup = (ClipTagGroup) param;
            // initialise tag interval to around current video time
            var tagStartTime = PlayerController.ClipPlayer.VideoTime - _settings.ClipLength / 2;
            // ensure start time is not less than zero
            tagStartTime = tagStartTime < 0 ? 0 : tagStartTime;
            var newTag = new ClipTag(tagStartTime, tagStartTime + _settings.ClipLength, tagGroup.ID); 
            var controller = new AddTagToGroupController(newTag, _session.VideoFile.Length);
            var view = new AddTagToGroupView();
            var window = CreateToolWindow(controller, view, "Add Tag to Group '" + tagGroup.Label + "'");
            if (window.ShowDialog() == true)
            {
                _session.ClipTags.Add(newTag);
                PopulateTagGroupViews();
            }
            _session.Save();
        }

        private bool CanAddTag(object param)
        {
            return param is ClipTagGroup;
        }

        #endregion // AddTag  
                
        #region RemoveCurrentTag

        private RelayCommand _removeCurrentTagCommand;

        /// <summary>
        /// Remove the CurrentClipTag object from the Session clip tag collection
        /// </summary>
        public ICommand RemoveCurrentTagCommand
        {
            get
            {
                if (_removeCurrentTagCommand == null)
                {
                    _removeCurrentTagCommand = new RelayCommand
                    (
                        param => this.RemoveCurrentTag(),
                        param => this.CanRemoveCurrentTag()
                    );
                }
                return _removeCurrentTagCommand;
            }
        }

        private void RemoveCurrentTag()
        {
            Session.ClipTags.Remove(CurrentClipTag);
            var matchingGroup = Session.ClipTagGroups.Where(tagGroup => tagGroup.ID == CurrentClipTag.GroupID).First();
            if (matchingGroup != null && matchingGroup.ClipTagsView.Contains(CurrentClipTag))
                ((ListCollectionView)matchingGroup.ClipTagsView).Remove(CurrentClipTag);
            CurrentClipTag = null;
        }

        private bool CanRemoveCurrentTag()
        {
           return CurrentClipTag != null && Session.ClipTags != null;
        }

        #endregion // RemoveCurrentTag  
                                    
        #endregion // Commands
    }
}
